@rubytech/create-maxy 1.0.477 → 1.0.478

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy",
3
- "version": "1.0.477",
3
+ "version": "1.0.478",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy": "./dist/index.js"
@@ -561,3 +561,26 @@ FOR (ag:AccessGrant) ON (ag.magicToken);
561
561
  // Status filtering — admin listing of active/expired/revoked grants.
562
562
  CREATE INDEX access_grant_status IF NOT EXISTS
563
563
  FOR (ag:AccessGrant) ON (ag.status);
564
+
565
+ // ----------------------------------------------------------
566
+ // AdminUser node — device-level admin identity
567
+ // Platform-native. Represents a human who administers one or
568
+ // more accounts via the admin agent. Exists at the device level
569
+ // (not account-scoped). Linked to accounts via ADMIN_OF.
570
+ //
571
+ // Properties:
572
+ // userId — UUID, matches entry in platform/config/users.json
573
+ // name — display name (e.g. "Adam")
574
+ // createdAt — ISO 8601 timestamp
575
+ //
576
+ // Relationships:
577
+ // (AdminUser)-[:ADMIN_OF {role, grantedAt}]->(LocalBusiness)
578
+ // role: "owner" | "admin" — label only, no enforcement
579
+ // grantedAt: ISO 8601 timestamp
580
+ //
581
+ // No vector index — AdminUser is always queried deterministically
582
+ // by userId. Semantic search over admin users is not a use case.
583
+ // ----------------------------------------------------------
584
+
585
+ CREATE CONSTRAINT admin_user_id_unique IF NOT EXISTS
586
+ FOR (au:AdminUser) REQUIRE au.userId IS UNIQUE;
@@ -5,7 +5,7 @@ description: Guide users through obtaining an Anthropic API key to power the pub
5
5
 
6
6
  # Anthropic / Claude Setup
7
7
 
8
- The public agent runs on the Anthropic API. This plugin handles obtaining, storing, and verifying an API key from console.anthropic.com.
8
+ The public agent runs on the Anthropic API. This plugin handles obtaining, storing, and verifying an API key from the Anthropic Console (platform.claude.com).
9
9
 
10
10
  ## When to activate
11
11
 
@@ -24,12 +24,13 @@ The public agent runs on the Anthropic API. This plugin handles obtaining, stori
24
24
 
25
25
  | Task | When to use | Skill |
26
26
  |------|-------------|-------|
27
- | Obtain API key via browser | `api-key-verify` returns `missing` or user wants to rotate key | `skills/get-api-key/SKILL.md` |
27
+ | Obtain API key | `api-key-verify` returns `missing` or user wants to rotate key | `skills/get-api-key/SKILL.md` |
28
28
 
29
29
  ## References
30
30
 
31
31
  | Topic | When to use | Reference |
32
32
  |-------|-------------|-----------|
33
33
  | Troubleshooting & FAQ | API key rejected, billing questions, manual key entry | `references/setup-guide.md` |
34
+ | Console API surface | Understanding the endpoints used by the get-api-key skill | `references/console-api.md` |
34
35
 
35
36
  For API key acquisition (including during onboarding), use the `skills/get-api-key/SKILL.md` skill. For troubleshooting and common questions, load the reference.
@@ -0,0 +1,186 @@
1
+ # Anthropic Console — Internal API Reference
2
+
3
+ Captured 2026-04-07 from `platform.claude.com` (formerly `console.anthropic.com`).
4
+
5
+ These are undocumented internal endpoints used by the Console's React frontend. They are not part of Anthropic's public API and may change without notice.
6
+
7
+ ## Authentication
8
+
9
+ Cookie-based via `sessionKey` cookie:
10
+ - Domain: `.platform.claude.com`
11
+ - Flags: `httpOnly`, `secure`, `SameSite=Lax`
12
+ - Expiry: 7 days from sign-in
13
+
14
+ The cookie is set during the OAuth sign-in flow (Google or email verification). Because it is `httpOnly`, it cannot be read by JavaScript — but `fetch()` with `credentials: 'include'` sends it automatically.
15
+
16
+ Common request headers (set by the Console frontend, not required for API auth):
17
+ - `content-type: application/json`
18
+ - `anthropic-client-platform: web_console`
19
+
20
+ ## Org Discovery
21
+
22
+ The Console is org-scoped. All billing and key management endpoints require an org UUID. A user may have multiple orgs (e.g., a consumer chat org and an API org).
23
+
24
+ ### GET /api/organizations
25
+
26
+ Returns all orgs the user belongs to.
27
+
28
+ ```json
29
+ [
30
+ {
31
+ "id": 55730262,
32
+ "uuid": "1882f41b-...",
33
+ "name": "Neo's Individual Org",
34
+ "billing_type": "prepaid",
35
+ "capabilities": ["api", "api_individual"],
36
+ "created_at": "2025-06-28T06:40:26.071676Z"
37
+ }
38
+ ]
39
+ ```
40
+
41
+ **Selecting the API org:** Filter for the org whose `capabilities` array contains `"api"`. Consumer orgs have `capabilities: ["claude_max", "chat"]` and no API key management.
42
+
43
+ ### GET /api/bootstrap
44
+
45
+ Returns current user account info and org memberships. Useful for getting the user's name and email.
46
+
47
+ ```json
48
+ {
49
+ "account": {
50
+ "tagged_id": "user_01YW5...",
51
+ "uuid": "ff1a722d-...",
52
+ "email_address": "user@example.com",
53
+ "full_name": "Neo Aeon",
54
+ "memberships": [
55
+ { "organization": { "uuid": "...", "name": "..." }, "role": "admin" }
56
+ ]
57
+ }
58
+ }
59
+ ```
60
+
61
+ ## Billing Endpoints
62
+
63
+ All GET, org-scoped. Replace `{orgId}` with the org UUID.
64
+
65
+ ### GET /api/organizations/{orgId}/prepaid/credits
66
+
67
+ Credit balance.
68
+
69
+ ```json
70
+ {
71
+ "amount": 504,
72
+ "currency": "USD",
73
+ "auto_reload_settings": {
74
+ "enabled": true,
75
+ "threshold_in_minor_units": 500,
76
+ "reload_to_in_minor_units": 1500
77
+ },
78
+ "pending_invoice_amount_cents": null
79
+ }
80
+ ```
81
+
82
+ `amount` is in **cents** (minor currency units). 504 = $5.04.
83
+
84
+ ### GET /api/organizations/{orgId}/current_spend
85
+
86
+ Current billing period spend.
87
+
88
+ ```json
89
+ {
90
+ "amount": 39,
91
+ "resets_at": "2026-05-01T00:00:00Z"
92
+ }
93
+ ```
94
+
95
+ `amount` is in **cents**. 39 = $0.39.
96
+
97
+ ### GET /api/organizations/{orgId}/prepaid/auto_recharge
98
+
99
+ Auto-recharge configuration.
100
+
101
+ ```json
102
+ {
103
+ "status": "active",
104
+ "stripe_error_code": null,
105
+ "target_amount": 1500,
106
+ "threshold_amount": 500
107
+ }
108
+ ```
109
+
110
+ Amounts in **cents**.
111
+
112
+ ### GET /api/organizations/{orgId}/payment_method
113
+
114
+ Payment method on file.
115
+
116
+ ```json
117
+ {
118
+ "brand": null,
119
+ "country": null,
120
+ "last4": null,
121
+ "type": "link"
122
+ }
123
+ ```
124
+
125
+ ## API Key Endpoints
126
+
127
+ ### GET /api/console/organizations/{orgId}/api_keys
128
+
129
+ List all API keys for the org.
130
+
131
+ ```json
132
+ [
133
+ {
134
+ "id": "apikey_01XY5wjo397BKKLjHNgvHATx",
135
+ "type": "api_key",
136
+ "name": "maxy",
137
+ "workspace_id": null,
138
+ "created_at": "2026-03-22T19:08:25.868735Z",
139
+ "created_by": { "id": "user_01YW5...", "type": "user" },
140
+ "partial_key_hint": "sk-ant-api03-kNh...HAAA",
141
+ "status": "active",
142
+ "expires_at": null
143
+ }
144
+ ]
145
+ ```
146
+
147
+ `status` values: `"active"`, `"inactive"` (disabled), `"archived"` (deleted).
148
+
149
+ ### POST /api/console/organizations/{orgId}/workspaces/default/api_keys
150
+
151
+ Create a new API key. The `raw_key` is only returned once at creation time.
152
+
153
+ **Request:**
154
+ ```json
155
+ { "name": "Maxy" }
156
+ ```
157
+
158
+ **Response (200):**
159
+ ```json
160
+ {
161
+ "id": "apikey_01Hkz...",
162
+ "type": "api_key",
163
+ "name": "Maxy",
164
+ "workspace_id": null,
165
+ "created_at": "2026-04-07T11:05:39.949811Z",
166
+ "created_by": { "id": "user_01YW5...", "type": "user" },
167
+ "partial_key_hint": "sk-ant-api03-cPs...1AAA",
168
+ "status": "active",
169
+ "expires_at": null,
170
+ "raw_key": "sk-ant-api03-cPsiih...ewev1AAA"
171
+ }
172
+ ```
173
+
174
+ ### POST /api/console/organizations/{orgId}/workspaces/default/api_keys/{keyId}
175
+
176
+ Update key status.
177
+
178
+ **Disable:**
179
+ ```json
180
+ { "status": "inactive" }
181
+ ```
182
+
183
+ **Archive (delete):**
184
+ ```json
185
+ { "status": "archived" }
186
+ ```
@@ -18,10 +18,10 @@ If the automated browser flow is unavailable or the user already has a key:
18
18
 
19
19
  | Problem | Solution |
20
20
  |---------|----------|
21
- | API key rejected | Verify it starts with `sk-ant-`. Check that billing is set up on console.anthropic.com — keys don't work without an active billing method. |
21
+ | API key rejected | Verify it starts with `sk-ant-`. Check that billing is set up on platform.claude.com — keys don't work without an active billing method. |
22
22
  | Key created on zero-balance account | Keys created before credits are loaded are permanently rejected. Create a new key after adding credits. |
23
23
  | "Which provider should I pick?" | Anthropic / Claude is the right choice. It powers all of the public agent's conversations and reasoning. |
24
- | Browser automation failed | Tell the user to get a key from console.anthropic.com/settings/keys in their own browser, then paste it in the chat. |
24
+ | Browser automation failed | Tell the user to get a key from platform.claude.com/settings/keys in their own browser, then paste it in the chat. |
25
25
 
26
26
  ---
27
27
 
@@ -32,4 +32,4 @@ If the automated browser flow is unavailable or the user already has a key:
32
32
  | "How much does the API cost?" | Anthropic charges per API call. For small business use, typical costs are a few dollars per month. See anthropic.com/pricing for current rates. |
33
33
  | "Do I need to restart after adding a key?" | No — the API key is applied immediately. |
34
34
  | "Can I rotate my key?" | Yes — run the `get-api-key` skill again or paste a new key. The old key is overwritten. |
35
- | "What if my key stops working?" | Check that billing is still active on console.anthropic.com. If the account ran out of credits, add more and the existing key should resume working (unless it was created on a zero-balance account). |
35
+ | "What if my key stops working?" | Check that billing is still active on platform.claude.com. If the account ran out of credits, add more and the existing key should resume working (unless it was created on a zero-balance account). |
@@ -1,51 +1,115 @@
1
1
  ---
2
2
  name: get-api-key
3
- description: Obtain an Anthropic API key by delegating browser automation to the browser-specialist subagent. Verifies credits are loaded before creating a key to prevent permanently rejected keys.
3
+ description: Obtain an Anthropic API key by calling the Console's internal API endpoints via browser_evaluate, falling back to screenshot-based browser automation if direct API calls fail.
4
4
  ---
5
5
 
6
6
  # Get Anthropic API Key
7
7
 
8
- Obtain an API key from console.anthropic.com on behalf of the user. Browser automation is delegated to `specialists:browser-specialist` a plugin-defined specialist subagent. All Playwright interaction runs in the specialist's isolated context, keeping the main session lightweight. The user can see the browser via the VNC viewer displayed in the chat.
8
+ Obtain an API key from the Anthropic Console (platform.claude.com) on behalf of the user. The browser-specialist calls Console API endpoints directly via `browser_evaluate` + `fetch()` the browser's session cookies handle auth automatically. This is orders of magnitude faster than navigating pages and interpreting screenshots.
9
9
 
10
10
  **Critical ordering rule:** Credits must be confirmed on the account before creating an API key. Keys created on a zero-balance account are permanently rejected by the Anthropic API — even after credits are added later.
11
11
 
12
+ **Auth model:** The Console uses an `httpOnly` session cookie (`sessionKey`) set during sign-in. Because it's `httpOnly`, JavaScript can't read it — but `fetch()` with `credentials: 'include'` sends it automatically. All API calls in the briefs below rely on this.
13
+
12
14
  ## Steps
13
15
 
14
16
  1. **Show the browser.** Call `render-component` with:
15
17
  - `name: "browser-viewer"`
16
18
  - `data: { title: "Anthropic Console" }`
17
19
 
18
- 2. **Dispatch browser-specialist for billing assessment.** Use the Agent tool with `specialists:browser-specialist` as the subagent type. Brief:
19
-
20
- > Navigate to https://console.anthropic.com/settings/billing. Take a screenshot to assess the page. Report what you see: Is there a credit balance shown? Does it say "Buy credits to get started"? Is a sign-in page shown instead? Report the billing status and any dollar amount visible. If sign-in is required, report "SIGN_IN_REQUIRED" and stop.
20
+ 2. **Dispatch browser-specialist for sign-in check and billing assessment.** Use the Agent tool with `specialists:browser-specialist` as the subagent type. Brief:
21
+
22
+ > Navigate to https://platform.claude.com/settings/billing. Wait for the page to load.
23
+ >
24
+ > Then use `browser_evaluate` to run this JavaScript:
25
+ > ```js
26
+ > (async () => {
27
+ > try {
28
+ > const orgsRes = await fetch('/api/organizations', { credentials: 'include', headers: { 'content-type': 'application/json' } });
29
+ > if (orgsRes.status === 401 || orgsRes.status === 403) return { status: 'SIGN_IN_REQUIRED' };
30
+ > const orgs = await orgsRes.json();
31
+ > const apiOrg = orgs.find(o => o.capabilities && o.capabilities.includes('api'));
32
+ > if (!apiOrg) return { status: 'NO_API_ORG', orgs: orgs.map(o => ({ name: o.name, capabilities: o.capabilities })) };
33
+ > const creditsRes = await fetch(`/api/organizations/${apiOrg.uuid}/prepaid/credits`, { credentials: 'include', headers: { 'content-type': 'application/json' } });
34
+ > if (!creditsRes.ok) return { status: 'BILLING_CHECK_FAILED', httpStatus: creditsRes.status };
35
+ > const credits = await creditsRes.json();
36
+ > return { status: 'OK', orgId: apiOrg.uuid, orgName: apiOrg.name, creditsCents: credits.amount, currency: credits.currency };
37
+ > } catch (e) { return { status: 'FETCH_ERROR', error: e.message }; }
38
+ > })()
39
+ > ```
40
+ >
41
+ > Report the result object exactly as returned. Do not interpret or summarize — pass the JSON back verbatim.
21
42
 
22
43
  3. **Handle the billing result.**
23
- - If the specialist reports a credit balance greater than $0: proceed to step 6 (create key).
24
- - If the specialist reports "SIGN_IN_REQUIRED": proceed to step 4.
25
- - If the specialist reports no credits, zero balance, or "Buy credits to get started": proceed to step 5.
26
- - If the specialist reports any other error (CAPTCHA, navigation failure, tool unavailable) or the billing status cannot be determined: warn the user that billing status could not be confirmed, and proceed to step 6 (create key). Do not block onboarding on an inconclusive check.
44
+ - `status: "OK"` with `creditsCents > 0`: Credits available. Note the `orgId` it's needed for key creation. Proceed to step 6.
45
+ - `status: "OK"` with `creditsCents === 0`: No credits. Proceed to step 5.
46
+ - `status: "SIGN_IN_REQUIRED"`: Proceed to step 4.
47
+ - `status: "NO_API_ORG"`: Tell the user: "Your Anthropic account doesn't have an API organization. Visit platform.claude.com and set up API access, then try again."
48
+ - `status: "BILLING_CHECK_FAILED"` or `status: "FETCH_ERROR"`: The direct API call failed. Fall back to the screenshot-based billing check (step 2F).
27
49
 
28
50
  4. **Sign-in required.** Tell the user: "Sign in to your Anthropic account in the browser. You can use Google sign-in or enter your email — Anthropic will send a verification email. Open the email in the same browser (open a new tab) to complete sign-in. If you don't have an account, create one — you will need to add credits before a key can be created." Wait for the user to confirm they have signed in. Then return to step 2 to re-check billing.
29
51
 
30
- 5. **No credits — guide the user.** Tell the user: "Your Anthropic account needs credits loaded before I can create an API key. Add credits or enable auto-top-up on the billing page. Let me know when you're done." Wait for confirmation, then dispatch browser-specialist to re-check the billing page (same brief as step 2). If credits are now present, proceed to step 6. If still no credits, repeat this step (maximum 3 attempts). After 3 attempts with no credits detected, tell the user: "Credits still not detected. You can add them at console.anthropic.com/settings/billing and then ask me to create the API key again."
31
-
32
- 6. **Dispatch browser-specialist for key creation.** Use the Agent tool with `specialists:browser-specialist` as the subagent type. Brief:
33
-
34
- > Navigate to https://console.anthropic.com/settings/keys. Take a screenshot to verify you are on the API Keys page. Create a new API key named "Maxy": find and click the "Create Key" button, fill the key name with "Maxy", submit the form. After creation, look for a string starting with sk-ant- on the page — Anthropic shows the key only once. If found, call api-key-store with the key. Report what happened. If sign-in is required, report "SIGN_IN_REQUIRED" and stop.
52
+ 5. **No credits — guide the user.** Tell the user: "Your Anthropic account needs credits loaded before I can create an API key. Add credits or enable auto-top-up on the billing page. Let me know when you're done." Wait for confirmation, then re-dispatch the billing check (step 2). If credits are now present, proceed to step 6. If still no credits, repeat this step (maximum 3 attempts). After 3 attempts with no credits detected, tell the user: "Credits still not detected. You can add them at platform.claude.com/settings/billing and then ask me to create the API key again."
53
+
54
+ 6. **Dispatch browser-specialist for key creation.** Use the Agent tool with `specialists:browser-specialist` as the subagent type. Include the `orgId` from step 3. Brief:
55
+
56
+ > Use `browser_evaluate` to run this JavaScript (replace ORG_ID with the orgId from the billing check):
57
+ > ```js
58
+ > (async () => {
59
+ > try {
60
+ > const res = await fetch('/api/console/organizations/ORG_ID/workspaces/default/api_keys', {
61
+ > method: 'POST',
62
+ > credentials: 'include',
63
+ > headers: { 'content-type': 'application/json' },
64
+ > body: JSON.stringify({ name: 'Maxy' })
65
+ > });
66
+ > if (res.status === 401 || res.status === 403) return { status: 'SIGN_IN_REQUIRED' };
67
+ > if (!res.ok) return { status: 'CREATE_FAILED', httpStatus: res.status, body: await res.text() };
68
+ > const key = await res.json();
69
+ > if (!key.raw_key) return { status: 'NO_RAW_KEY', keyId: key.id };
70
+ > return { status: 'KEY_CREATED', rawKey: key.raw_key, keyName: key.name, keyId: key.id };
71
+ > } catch (e) { return { status: 'FETCH_ERROR', error: e.message }; }
72
+ > })()
73
+ > ```
74
+ >
75
+ > If the result has `status: "KEY_CREATED"`, call `api-key-store` with the `rawKey` value. Report the full result object back.
76
+ >
77
+ > If the result has any other status, report it back verbatim — do not attempt to navigate the Console UI.
35
78
 
36
79
  7. **Handle the key creation result.**
37
- - If the specialist reports the key was stored: proceed to step 9 (verify).
38
- - If the specialist reports "SIGN_IN_REQUIRED": this is unexpected since sign-in was handled during the billing check. Tell the user to sign in, wait for confirmation, then retry step 6 once. If it fails again, proceed to step 8 (fallback).
39
- - If the specialist reports any other error (CAPTCHA, key not extractable, navigation failure): proceed to step 8 (fallback).
80
+ - `status: "KEY_CREATED"` and specialist confirms key was stored: proceed to step 9 (verify).
81
+ - `status: "SIGN_IN_REQUIRED"`: this is unexpected since sign-in was handled during the billing check. Tell the user to sign in, wait for confirmation, then retry step 6 once. If it fails again, proceed to step 8 (fallback).
82
+ - `status: "CREATE_FAILED"` or `status: "FETCH_ERROR"`: The direct API call failed. Fall back to the screenshot-based key creation (step 6F).
83
+ - `status: "NO_RAW_KEY"`: The key was created but the response didn't include the full key value. Proceed to step 8 (manual fallback).
40
84
 
41
85
  8. **Fallback.** Tell the user: "I could not extract the key automatically. Please copy it from the browser and paste it here in the chat." When the user pastes a key starting with `sk-ant-`, call `api-key-store` with it. Then proceed to step 9.
42
86
 
43
87
  9. **Verify the key works.** Call the `api-key-verify` MCP tool. Handle each status:
44
88
  - `valid`: Confirm to the user — "API key stored and verified. Your public agent is ready."
45
89
  - `billing`: Credits were confirmed before key creation, so this is unexpected. Tell the user: "The key was stored but credits may not have propagated yet. Please wait a moment." Retry `api-key-verify` once after a brief pause. If still `billing`, tell the user: "Credits are not being detected yet. The key is stored — the public agent will start working once Anthropic recognises the credits on your account."
46
- - `auth_error`: The key was rejected. Tell the user: "The stored key was rejected by Anthropic. It may have been copied incorrectly. Let's try again." Return to step 6 (maximum 2 retries — if it fails 3 times total, tell the user to visit console.anthropic.com/settings/keys in their own browser and paste the key manually).
90
+ - `auth_error`: The key was rejected. Tell the user: "The stored key was rejected by Anthropic. It may have been copied incorrectly. Let's try again." Return to step 6 (maximum 2 retries — if it fails 3 times total, tell the user to visit platform.claude.com/settings/keys in their own browser and paste the key manually).
47
91
  - `error`: Could not verify (network or Anthropic issue). Tell the user: "I couldn't verify the key right now — this is likely a temporary issue. The key is stored and should work once the connection is restored."
48
92
 
93
+ ## Fallback Steps (screenshot-based)
94
+
95
+ These steps are used when `browser_evaluate` + `fetch()` fails. They replicate the original browser automation approach.
96
+
97
+ ### Step 2F: Screenshot-based billing check
98
+
99
+ Dispatch browser-specialist with brief:
100
+
101
+ > Navigate to https://platform.claude.com/settings/billing. Take a screenshot to assess the page. Report what you see: Is there a credit balance shown? Does it say "Buy credits to get started"? Is a sign-in page shown instead? Report the billing status and any dollar amount visible. If sign-in is required, report "SIGN_IN_REQUIRED" and stop.
102
+
103
+ Handle the result the same as step 3, mapping the specialist's text observations to the structured statuses.
104
+
105
+ ### Step 6F: Screenshot-based key creation
106
+
107
+ Dispatch browser-specialist with brief:
108
+
109
+ > Navigate to https://platform.claude.com/settings/keys. Take a screenshot to verify you are on the API Keys page. Create a new API key named "Maxy": find and click the "Create Key" button, fill the key name with "Maxy", submit the form. After creation, look for a string starting with sk-ant- on the page — Anthropic shows the key only once. If found, call api-key-store with the key. Report what happened. If sign-in is required, report "SIGN_IN_REQUIRED" and stop.
110
+
111
+ Handle the result the same as step 7.
112
+
49
113
  ## Important
50
114
 
51
115
  - **`render-component` is required before any browser-specialist dispatch.** The browser-viewer component is what makes the VNC browser visible to the user. Without it, automation runs invisibly. Always call `render-component` with `name: "browser-viewer"` and confirm it renders before dispatching a browser-specialist subagent.
@@ -53,5 +117,10 @@ Obtain an API key from console.anthropic.com on behalf of the user. Browser auto
53
117
  - The browser is visible to the user via VNC. They can see everything the specialist does.
54
118
  - The user must sign in themselves — the specialist does not fill credentials.
55
119
  - For email verification: tell the user to open the verification email in a new tab in the same VNC browser (not on their own device). This keeps the sign-in session in the same browser context.
56
- - Anthropic shows the API key only once after creation. If the user navigates away before it is captured, they must create a new key.
57
- - If the browser-specialist is not available (not installed, Agent tool fails), skip the browser-viewer component entirely and tell the user to get a key from console.anthropic.com in their own browser, then paste it in the chat.
120
+ - Anthropic shows the API key only once after creation. With the direct API approach, the `raw_key` is in the JSON response — but if the specialist fails to call `api-key-store` before its context ends, the key is lost and a new one must be created.
121
+ - If the browser-specialist is not available (not installed, Agent tool fails), skip the browser-viewer component entirely and tell the user to get a key from platform.claude.com in their own browser, then paste it in the chat.
122
+ - The Console URL `console.anthropic.com` redirects to `platform.claude.com`. All briefs use `platform.claude.com` to avoid redirect latency.
123
+
124
+ ## API Reference
125
+
126
+ For the complete captured API surface (endpoint details, response schemas, auth mechanism), see `references/console-api.md`.
@@ -222,6 +222,75 @@ fi
222
222
 
223
223
  echo " Account $ACCOUNT_ID at $ACCOUNT_DIR"
224
224
 
225
+ # ------------------------------------------------------------------
226
+ # 1b. Create first admin user (migration from .admin-pin)
227
+ # ------------------------------------------------------------------
228
+
229
+ CONFIG_DIR="$PROJECT_DIR/config"
230
+ USERS_FILE="$CONFIG_DIR/users.json"
231
+
232
+ # Resolve the brand-specific config directory for .admin-pin location.
233
+ # Mirrors the logic in platform/ui/app/lib/paths.ts.
234
+ _CONFIG_DIR_NAME=".maxy"
235
+ if [ -f "$CONFIG_DIR/brand.json" ]; then
236
+ _BRAND_CFG_DIR=$(python3 -c "import json; print(json.load(open('$CONFIG_DIR/brand.json')).get('configDir',''))" 2>/dev/null || true)
237
+ [ -n "$_BRAND_CFG_DIR" ] && _CONFIG_DIR_NAME="$_BRAND_CFG_DIR"
238
+ fi
239
+ ADMIN_PIN_FILE="$HOME/$_CONFIG_DIR_NAME/.admin-pin"
240
+
241
+ # Only create users.json if it doesn't exist AND .admin-pin exists (migration case).
242
+ # Fresh installs: no .admin-pin yet (set during onboarding). The session route falls
243
+ # back to .admin-pin when users.json is absent — no migration needed until next seed.
244
+ if [ ! -f "$USERS_FILE" ] && [ -f "$ADMIN_PIN_FILE" ]; then
245
+ USER_ID="$(cat /proc/sys/kernel/random/uuid 2>/dev/null || python3 -c 'import uuid; print(uuid.uuid4())')"
246
+ PIN_HASH="$(cat "$ADMIN_PIN_FILE")"
247
+
248
+ # Create users.json with the first user entry.
249
+ # PINs are stored as SHA-256 hashes — same format as .admin-pin.
250
+ cat > "$USERS_FILE" << USERS_EOF
251
+ [{"userId":"$USER_ID","name":"Owner","pin":"$PIN_HASH"}]
252
+ USERS_EOF
253
+ echo " [seed] created initial user: userId=$USER_ID"
254
+
255
+ # Add the user to account.json's admins array.
256
+ # The template includes "admins": [] — populate it with the owner entry.
257
+ if [ -f "$ACCOUNT_DIR/account.json" ]; then
258
+ python3 -c "
259
+ import json, sys
260
+ with open('$ACCOUNT_DIR/account.json', 'r') as f:
261
+ config = json.load(f)
262
+ config.setdefault('admins', [])
263
+ if not any(a.get('userId') == '$USER_ID' for a in config['admins']):
264
+ config['admins'].append({'userId': '$USER_ID', 'role': 'owner'})
265
+ with open('$ACCOUNT_DIR/account.json', 'w') as f:
266
+ json.dump(config, f, indent=2)
267
+ f.write('\n')
268
+ "
269
+ echo " Added userId=$USER_ID as owner to account.json admins"
270
+ fi
271
+ elif [ -f "$USERS_FILE" ]; then
272
+ # Re-run: users.json exists. Read the first user's ID for Neo4j seeding.
273
+ USER_ID=$(python3 -c "import json; print(json.load(open('$USERS_FILE'))[0]['userId'])" 2>/dev/null || true)
274
+ [ -n "$USER_ID" ] && echo " Existing users.json found, userId=$USER_ID"
275
+
276
+ # Backfill admins in account.json if missing (existing accounts pre-Task 248).
277
+ if [ -f "$ACCOUNT_DIR/account.json" ] && ! grep -q '"admins"' "$ACCOUNT_DIR/account.json"; then
278
+ python3 -c "
279
+ import json
280
+ with open('$ACCOUNT_DIR/account.json', 'r') as f:
281
+ config = json.load(f)
282
+ config['admins'] = [{'userId': '$USER_ID', 'role': 'owner'}]
283
+ with open('$ACCOUNT_DIR/account.json', 'w') as f:
284
+ json.dump(config, f, indent=2)
285
+ f.write('\n')
286
+ "
287
+ echo " Backfilled admins array in account.json"
288
+ fi
289
+ else
290
+ echo " No .admin-pin found — skipping users.json creation (fresh install)"
291
+ USER_ID=""
292
+ fi
293
+
225
294
  # ------------------------------------------------------------------
226
295
  # 2. Apply Neo4j schema (constraints + indexes only)
227
296
  # ------------------------------------------------------------------
@@ -236,4 +305,27 @@ echo "==> Applying schema (constraints, indexes, vector indexes)..."
236
305
  "$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" \
237
306
  -f "$NEO4J_DIR/schema.cypher"
238
307
 
308
+ # ------------------------------------------------------------------
309
+ # 3. Create AdminUser node + ADMIN_OF relationship
310
+ # ------------------------------------------------------------------
311
+
312
+ if [ -n "${USER_ID:-}" ]; then
313
+ # Escape single quotes for Cypher string interpolation (e.g. O'Brien → O''Brien).
314
+ USER_NAME=$(python3 -c "import json; n=json.load(open('$USERS_FILE'))[0]['name']; print(n.replace(\"'\",\"''\"))" 2>/dev/null || echo "Owner")
315
+ CREATED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
316
+
317
+ echo "==> Creating AdminUser node for userId=$USER_ID"
318
+ "$CYPHER_SHELL" -u "$NEO4J_USER" -p "$NEO4J_PASSWORD" -a "$NEO4J_URI" << CYPHER_EOF
319
+ MERGE (au:AdminUser {userId: '$USER_ID'})
320
+ ON CREATE SET au.name = '$USER_NAME', au.createdAt = '$CREATED_AT'
321
+ ON MATCH SET au.name = '$USER_NAME'
322
+ WITH au
323
+ MATCH (b:LocalBusiness {accountId: '$ACCOUNT_ID'})
324
+ MERGE (au)-[r:ADMIN_OF]->(b)
325
+ ON CREATE SET r.role = 'owner', r.grantedAt = '$CREATED_AT'
326
+ RETURN au.userId, b.accountId;
327
+ CYPHER_EOF
328
+ echo " AdminUser node created/updated"
329
+ fi
330
+
239
331
  echo " Done."
@@ -9,5 +9,6 @@
9
9
  "contextMode": "claude-code",
10
10
  "enabledPlugins": [],
11
11
  "purchasedPlugins": [],
12
- "defaultAgent": ""
12
+ "defaultAgent": "",
13
+ "admins": []
13
14
  }